/* ----------------------------------------------------------------------------
 * Copyright (c) Huawei Technologies Co., Ltd. 2013-2019. All rights reserved.
 * Description: A7 Mmu Inner HeadFile
 * Author: dongruofan
 * Create: 2013-01-01
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior written
 * permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which might
 * include those applicable to Huawei LiteOS of U.S. and the country in which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance with such
 * applicable export control laws and regulations.
 * --------------------------------------------------------------------------- */
#ifndef _MMU_H
#define _MMU_H

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */

#define UNCACHEABLE    0
#define CACHEABLE      1
#define UNBUFFERABLE   0
#define BUFFERABLE     1
#define EXECUTABLE     0
#define NON_EXECUTABLE 1
#define ACCESS_RW      3 // ap = 0 ap1 = 0b11
#define ACCESS_RO      7 // ap = 1 ap1 = 0b11
#define ACCESS_NA      0 // ap = 0 ap1 = 0
#define D_MANAGER      0
#define D_CLIENT       1
#define D_NA           2

#define MMU_AP_STATE(flag)      (((flag) & 0x1) ? ACCESS_RW : ACCESS_RO)
#define MMU_CACHE_STATE(flag)   (((flag) >> 1) & 0x1)
#define MMU_BUFFER_STATE(flag)  (((flag) >> 2) & 0x1)
#define MMU_EXECUTE_STATE(flag) (((flag) >> 3) & 0x1)
#define MMU_GET_AREA(flag)      ((flag) & (0x1 << 4))

/**
 * @ingroup mmu
 * The access permission mode is read-only.
 */
#define ACCESS_PERM_RO_RO 0

/**
 * @ingroup mmu
 * The access permission mode is read and write.
 */
#define ACCESS_PERM_RW_RW (1 << 0)

/**
 * @ingroup mmu
 * The cache enabled.
 */
#define CACHE_ENABLE (1 << 1)

/**
 * @ingroup mmu
 * The cache disabled.
 */
#define CACHE_DISABLE 0

/**
 * @ingroup mmu
 * The buffer enabled.
 */
#define BUFFER_ENABLE (1 << 2)

/**
 * @ingroup mmu
 * The buffer disabled.
 */
#define BUFFER_DISABLE 0

/**
 * @ingroup mmu_config
 * Set it non-executable.
 */
#define EXEC_DISABLE (1 << 3)

/**
 * @ingroup mmu_config
 * Set it executable.
 */
#define EXEC_ENABLE 0

/**
 * @ingroup mmu
 * The first section(1M/item).
 */
#define FIRST_SECTION (1 << 4)

/**
 * @ingroup mmu
 * The second page(4K/item).
 */
#define SECOND_PAGE 0

#define MMU_1K  0x400
#define MMU_4K  0x1000
#define MMU_16K 0x4000
#define MMU_64K 0x10000
#define MMU_1M  0x100000
#define MMU_4G  0x100000000ull

#define SHIFT_1K  10
#define SHIFT_4K  12
#define SHIFT_16K 14
#define SHIFT_64K 16
#define SHIFT_1M  20
#define SHIFT_2M  21
#define SHIFT_1G  30

/**
 * @ingroup mmu
 * mmu second page information structure.
 *
 */
typedef struct {
    UINT32 page_addr;            /* The second page start addr       */
    UINT32 page_length;          /* The second page length             */

    /* The second page page table storage addr, diff second page table can't be coincided */
    UINT32 page_descriptor_addr;

    /* The second page type, it can be set small page ID(4K):MMU_SECOND_LEVEL_SMALL_PAGE_TABLE_ID
     * or big page ID(64K):MMU_SECOND_LEVEL_BIG_PAGE_TABLE_ID
     */
    UINT32 page_type;
} SENCOND_PAGE;

/**
 * @ingroup mmu
 * mmu param setting information structure.
 *
 */
typedef struct {
    UINT32 startAddr; /* Starting address of a section.   */
    UINT32 endAddr;   /* Ending address of a section.     */

    /* mode set. There are four func could be controlled with four bit.
     * bit0: ACCESS_PERM_RW_RW/ACCESS_PERM_RO_RO(1/0)
     * bit1: CACHE_ENABLE/CACHE_DISABLE(1/0)
     * bit2: BUFFER_ENABLE/BUFFER_DISABLE(1/0)
     * bit3: EXEC_DISENABLE/EXEC_ENABLE(1/0)
     * bit4: FIRST_SECTION/SECOND_PAGE(1/0) it need comfire your memory type, be descripted
     * bit5~7: ignore
     */
    UINT32 uwFlag;

    /*  the goal object of second page,
     * if uwFlag bit4 is FIRST_SECTION, stPage will be ignored, and you can set this member as NULL
     */
    SENCOND_PAGE *stPage;
} MMU_PARAM;

// ARM Domain Access Control Bit Masks
#define ACCESS_TYPE_NO_ACCESS(domainNum) (0x0 << ((domainNum) * 2))
#define ACCESS_TYPE_CLIENT(domainNum)    (0x1 << ((domainNum) * 2))
#define ACCESS_TYPE_MANAGER(domainNum)   (0x3 << ((domainNum) * 2))

#define MMU_FIRST_LEVEL_FAULT_ID      0x0
#define MMU_FIRST_LEVEL_PAGE_TABLE_ID 0x1
#define MMU_FIRST_LEVEL_SECTION_ID    0x2
#define MMU_FIRST_LEVEL_RESERVED_ID   0x3
/**
 * @ingroup mmu
 * The second page type select 64K
 */
#define MMU_SECOND_LEVEL_BIG_PAGE_TABLE_ID 0x1

/**
 * @ingroup mmu
 * The second page type select 4K
 */
#define MMU_SECOND_LEVEL_SMALL_PAGE_TABLE_ID 0x2

struct MMUFirstLevelFault {
    UINT32 id : 2;
    UINT32 sbz : 30;
};

struct MMUFirstLevelPageTable {
    UINT32 id : 2;
    UINT32 pxn : 1;
    UINT32 ns : 1;
    UINT32 sbz : 1;
    UINT32 domain : 4;
    UINT32 imp : 1;
    UINT32 baseAddress : 22;
};

struct MMUSecondLevelBigPageTable {
    UINT32 id : 2;
    UINT32 b : 1;
    UINT32 c : 1;
    UINT32 ap1 : 2;
    UINT32 sbz : 3;
    UINT32 ap : 1;
    UINT32 s : 1;
    UINT32 ng : 1;
    UINT32 tex : 3;
    UINT32 xn : 1;
    UINT32 baseAddress : 16;
};

struct MMUSecondLevelSmallPageTable {
    UINT32 xn : 1;
    UINT32 id : 1;
    UINT32 b : 1;
    UINT32 c : 1;
    UINT32 ap1 : 2;
    UINT32 tex : 3;
    UINT32 ap : 1;
    UINT32 s : 1;
    UINT32 ng : 1;
    UINT32 baseAddress : 20;
};

struct MMUFirstLevelSection {
    UINT32 id : 2;
    UINT32 b : 1;
    UINT32 c : 1;
    UINT32 xn : 1;
    UINT32 domain : 4;
    UINT32 imp : 1;
    UINT32 ap1 : 2;
    UINT32 tex : 3;
    UINT32 ap : 1;
    UINT32 s : 1;
    UINT32 ng : 1;
    UINT32 revs : 1;
    UINT32 ns : 1;
    UINT32 baseAddress : 12;
};

struct MMUFirstLevelReserved {
    UINT32 id : 2;
    UINT32 sbz : 30;
};

#define SECTION_CHANGE(item, cache, buff, access, exeNever)  \
    do {                                                     \
        union MMUFirstLevelDescriptor desc;                  \
        desc.word = (*(AARCHPTR *)(item));                   \
        desc.section.c = (cache);                            \
        desc.section.b = (buff);                             \
        desc.section.ap1 = (access) & 0x3;                   \
        desc.section.ap = (access) >> 2;                     \
        desc.section.xn = (exeNever);                        \
        (*(AARCHPTR *)(item)) = desc.word;                   \
    } while (0)

#define X_MMU_SECTION(abase, vbase, size, cache, buff, access, exeNever, sdomain)  \
    do {                                                                           \
        UINT32 i;                                                                  \
        UINT32 j = (abase);                                                        \
        UINT32 k = (vbase);                                                        \
        union MMUFirstLevelDescriptor desc;                                        \
        desc.word = 0;                                                             \
        desc.section.id = MMU_FIRST_LEVEL_SECTION_ID;                              \
        desc.section.b = (buff);                                                   \
        desc.section.c = (cache);                                                  \
        desc.section.xn = (exeNever);                                              \
        desc.section.domain = (sdomain);                                           \
        desc.section.ap1 = (access) & 0x3;                                         \
        desc.section.ap = (access) >> 2;                                           \
        k <<= 2;                                                                   \
        k += ttb_base;                                                             \
        for (i = 0; i < (size); ++i, ++j, k += 4) {                                \
            desc.section.baseAddress = (j);                                        \
            *(AARCHPTR *)k = desc.word;                                            \
        }                                                                          \
    } while (0)

#define X_MMU_ONE_LEVEL_PAGE(abase, vbase, size, sdomain)   \
    do {                                                    \
        UINT32 i;                                           \
        UINT32 j = abase;                                   \
        UINT32 k = vbase;                                   \
        union MMUFirstLevelDescriptor desc;                 \
        desc.word = 0;                                      \
        desc.pageTable.id = MMU_FIRST_LEVEL_PAGE_TABLE_ID;  \
        desc.pageTable.domain = (sdomain);                  \
        k <<= 2;                                            \
        k += ttb_base;                                      \
        for (i = 0; i < (size); ++i, ++j, k += 4) {         \
            desc.pageTable.baseAddress = (j);               \
            *(AARCHPTR *)k = desc.word;                     \
        }                                                   \
    } while (0)

#define X_MMU_TWO_LEVEL_PAGE(abase, vbase, size, cache, buff, access, exeNever)  \
    do {                                                                         \
        UINT32 i;                                                                \
        UINT32 j = (abase);                                                      \
        UINT32 k = (vbase);                                                      \
        union MMUFirstLevelDescriptor desc;                                      \
        desc.word = 0;                                                           \
        desc.smallPageTable.xn = (exeNever);                                     \
        desc.smallPageTable.id = MMU_SECOND_LEVEL_SMALL_PAGE_TABLE_ID >> 1;      \
        desc.smallPageTable.b = (buff);                                          \
        desc.smallPageTable.c = (cache);                                         \
        desc.smallPageTable.ap1 = (access) & 0x3;                                \
        desc.smallPageTable.ap = (access) >> 2;                                  \
        k <<= 2;                                                                 \
        k += stt_base;                                                           \
        for (i = 0; i < (size); ++i, ++j, k += 4) {                              \
            desc.smallPageTable.baseAddress = (j);                               \
            *(AARCHPTR *)k = desc.word;                                          \
        }                                                                        \
    } while (0)

#define X_MMU_TWO_LEVEL_PAGE64K(abase, vbase, size, cache, buff, access, exeNever)  \
    do {                                                                            \
        UINT32 i;                                                                   \
        UINT32 j = (abase);                                                         \
        UINT32 k = (vbase);                                                         \
        UINT32 n;                                                                   \
        union MMUFirstLevelDescriptor desc;                                         \
        desc.word = 0;                                                              \
        desc.bigPageTable.id = MMU_SECOND_LEVEL_BIG_PAGE_TABLE_ID;                  \
        desc.bigPageTable.b = (buff);                                               \
        desc.bigPageTable.c = (cache);                                              \
        desc.bigPageTable.ap1 = (access) & 0x3;                                     \
        desc.bigPageTable.ap = (access) >> 2;                                       \
        desc.bigPageTable.xn = (exeNever);                                          \
        k <<= 2;                                                                    \
        k += stt_base;                                                              \
        for (i = 0; i < (size); ++i, ++j) {                                         \
            desc.bigPageTable.baseAddress = (j);                                    \
            for (n = 0; n < 16; ++n, k += 4) {                                      \
                *(AARCHPTR *)k = desc.word;                                         \
            }                                                                       \
        }                                                                           \
    } while (0)

union MMUFirstLevelDescriptor {
    unsigned long word;
    struct MMUFirstLevelFault fault;
    struct MMUFirstLevelPageTable pageTable;
    struct MMUFirstLevelSection section;
    struct MMUFirstLevelReserved reserved;
    struct MMUSecondLevelSmallPageTable smallPageTable;
    struct MMUSecondLevelBigPageTable bigPageTable;
};

static inline void EnableAPCheck(void)
{
    UINT32 regDACR =
        ACCESS_TYPE_MANAGER(0) |
        ACCESS_TYPE_CLIENT(1);
    __asm volatile("mcr    p15, 0, %0, c3, c0, 0"
                   :
                   : "r"(regDACR));
} /*lint !e529*/

static inline void DisableAPCheck(void)
{
    UINT32 regDACR =
        ACCESS_TYPE_MANAGER(0) |
        ACCESS_TYPE_MANAGER(1);
    __asm volatile("mcr    p15, 0, %0, c3, c0, 0"
                   :
                   : "r"(regDACR));
} /*lint !e529*/

static inline void CleanTLB(void)
{
    __asm volatile("mov    %0, #0\n"
                   "mcr    p15, 0, %0, c8, c7, 0\n"
                   :
                   : "r"(0));
}

#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */

#endif /* _MMU_H */
